/*
 * KKStoryboard.cpp
 *
 *  Created on: 2013-9-15
 *      Author: wanghua
 */

#include "KKStoryboard.h"
#include "KKDirector.h"
#include <iostream>
using namespace std;

USING_NS_CC;

KKStoryboard::KKStoryboard() {
	m_animations = new KKAnimationArray();

}

KKStoryboard::~KKStoryboard() {
	CC_SAFE_RELEASE(m_animations);
}

KKStoryboard* KKStoryboard::storyboardWithXml(TiXmlElement* element, int tag) {
	KKStoryboard* storyboard = new KKStoryboard();

	if (storyboard->initWithXml(element, tag)) {
		storyboard->retain();
		storyboard->autorelease();
		return storyboard;
	}

	CC_SAFE_DELETE(storyboard);

	return NULL;
}

bool KKStoryboard::initWithXml(TiXmlElement* element, int tag) {

	this->m_tag = tag;
	this->m_name = element->Attribute("name");

	TiXmlElement* animationElment = element->FirstChildElement("animation");

	KKAnimation* animation = NULL;

	while (animationElment) {
		animation = KKAnimation::animationWithXml(animationElment);
		if (animation) {
			animation->setTag(tag);
			this->m_animations->addObject(animation);
		}
		animationElment = animationElment->NextSiblingElement("animation");
	}

	return this->m_animations->count() > 0;
}

void KKStoryboard::start() {

	KKAnimationArray::CCMutableArrayIterator it = this->m_animations->begin();

	KKAnimation* animation = NULL;

	CCNode* target = NULL;

	while (it != this->m_animations->end()) {

		animation = (KKAnimation*) (*it);

		target = animation->getTarget();

		if (target) {
			animation->start(target);
		}

		it++;
	}
}

void KKStoryboard::pause() {

	KKAnimationArray::CCMutableArrayIterator it = this->m_animations->begin();

	KKAnimation* animation = NULL;

	CCNode* target = NULL;

	while (it != this->m_animations->end()) {

		animation = (KKAnimation*) (*it);

		target = animation->getTarget();

		if (target) {

			CCDirector::sharedDirector()->getActionManager()->pauseTarget(
					target);
		}

		it++;
	}
}

void KKStoryboard::resume() {

	KKAnimationArray::CCMutableArrayIterator it = this->m_animations->begin();

	KKAnimation* animation = NULL;

	CCNode* target = NULL;

	while (it != this->m_animations->end()) {

		animation = (KKAnimation*) (*it);

		target = animation->getTarget();

		if (target) {
			CCDirector::sharedDirector()->getActionManager()->resumeTarget(
					target);
		}

		it++;
	}
}

void KKStoryboard::stop() {

	KKAnimationArray::CCMutableArrayIterator it = this->m_animations->begin();

	KKAnimation* animation = NULL;

	CCNode* target = NULL;

	while (it != this->m_animations->end()) {

		animation = (KKAnimation*) (*it);

		target = animation->getTarget();

		if (target) {
			target->stopActionByTag(animation->getTag());
		}

		it++;
	}
}

bool KKStoryboard::isRunning() {
	return numberOfRunningTarget() > 0;
}

int KKStoryboard::numberOfRunningTarget() {
	int count = 0;

	KKAnimationArray::CCMutableArrayIterator it = this->m_animations->begin();

	KKAnimation* animation = NULL;

	CCNode* target = NULL;

	while (it != this->m_animations->end()) {

		animation = (KKAnimation*) (*it);

		target = animation->getTarget();

		if (target && target->getActionByTag(animation->getTag())) {
			++count;
		}

		it++;
	}

	return count;
}

KKAnimation::KKAnimation() {
	m_frames = new KKFrameArray();
	this->m_endEvent = NULL;
	this->m_startEvent = NULL;
}

KKAnimation::~KKAnimation() {
	CC_SAFE_RELEASE(m_endEvent);
	CC_SAFE_RELEASE(m_startEvent);
	CC_SAFE_RELEASE(m_frames);
}

CCNode* KKAnimation::getTarget() {
	KKDirector* mapLayer = KKDirector::shareDirector();

	return dynamic_cast<CCNode*>(mapLayer->findNodeById(m_targetId));

}
void KKAnimation::start(CCNode* target, bool isStartWithReverse) {

	KKSprite* sprite = dynamic_cast<KKSprite*>(target);
	KKNode* kknode = dynamic_cast<KKNode*>(target);
	CCRGBAProtocol* rgbaProtocol = dynamic_cast<CCRGBAProtocol*>(target);

	CCArray* actions = CCArray::create();

	KKFrameArray::CCMutableArrayIterator it = this->m_frames->begin();

	KKStoryboardFrame* frame = NULL;

	float x, y, rotate, anchorX, anchorY, skewX, skewY, scaleX, scaleY;

	bool visible = true;

	bool prevVisible = true;

	float prevX, prevY, prevRotate, prevAnchorX, prevSkewX, prevSkewY,
			prevAnchorY, prevScaleX, prevScaleY;

	GLubyte alpha, prevAlpha;

	string status, prevStatus;

	prevX = target->getPosition().x;
	prevY = target->getPosition().y;
	prevAnchorX = target->getAnchorPoint().x;
	prevAnchorY = target->getAnchorPoint().y;
	prevRotate = target->getRotation();
	prevScaleX = target->getScaleX();
	prevScaleY = target->getScaleY();

	prevSkewX = target->getSkewX();
	prevSkewY = target->getSkewY();

	prevVisible = target->isVisible();

	if (sprite) {
		prevAlpha = sprite->getOpacity();
	}

	float duration = 0;
	float time = 0;

	bool isFirst = true;

	CCArray* subActions = CCArray::create();

	while (it != this->m_frames->end()) {
		frame = (KKStoryboardFrame*) (*it);
		duration = frame->getTime() - time;

		x = frame->getPositionX(kknode, prevX);
		y = frame->getPositionY(kknode, prevY);

		rotate = frame->getAngle();
		anchorX = frame->getAnchorX();
		anchorY = frame->getAnchorY();
		scaleX = frame->getWidth() / target->getContentSize().width;
		scaleY = frame->getHeight() / target->getContentSize().height;
		status = frame->getStatus();
		skewX = frame->getSkewX();
		skewY = frame->getSkewY();

		visible = frame->getIsEnabled() ? frame->getIsVisible() : prevVisible;

		if (rgbaProtocol) {
			alpha = frame->getAlpha();
		}

		if (isFirst) {

			actions->addObject(
					CCSequence::create(CCDelayTime::create(duration),
							CCCallFuncND::create(this,
							callfuncND_selector(
									KKAnimation::onStartAnimation), frame),
							NULL));

			isFirst = false;

		} else {

			if (prevX != x || prevY != y) {

				subActions->addObject(CCMoveBy::create(duration,
				ccp(x - prevX, y - prevY)));

			}

			if (prevScaleX != scaleX || prevScaleY != scaleY) {
				subActions->addObject(
				CCScaleBy::create(duration,
						scaleX / prevScaleX, scaleY / prevScaleY));
			}

			if (prevSkewX != skewX || prevSkewY != skewY) {
				subActions->addObject(
				CCSkewBy::create(duration,
						skewX - prevSkewX, skewY - prevSkewY));
			}

			if (prevRotate != rotate) {
				subActions->addObject(
				CCRotateBy::create(duration,
						rotate - prevRotate));
			}

			if (alpha != prevAlpha) {
				subActions->addObject(
				CCFadeTo::create(duration, alpha));
			}

			bool needUpdateCall = prevAnchorX != anchorX || prevAnchorY
			!= anchorY || prevStatus != status || visible
			!= prevVisible || frame->getEvent();

			if (needUpdateCall) {

				//cout << "delay call " << duration << endl;

				subActions->addObject(
				CCSequence::create(
						CCDelayTime::create(duration),
						CCCallFuncND::create(
								this,
								callfuncND_selector(
										KKAnimation::onUpdateSpriteAnimation),
								frame), NULL));
			} else if (subActions->count() == 0) {
				subActions->addObject(
				CCSequence::create(
						CCDelayTime::create(duration), NULL));
			}

			CCAction* frameAction = NULL;

			if (subActions->count() > 1) {
				frameAction = CCSpawn::create(subActions);
			} else if (subActions->count() == 1) {
				frameAction = (CCAction*) subActions->lastObject();

			}

			if (frameAction) {

				CCActionEase* easeAction = getActionEase(frame, frameAction);

				if (easeAction) {
					actions->addObject(easeAction);
				} else {
					actions->addObject(frameAction);
				}

			}

			subActions->removeAllObjects();

		}

		prevX = x;
		prevY = y;
		prevRotate = rotate;
		prevAnchorX = anchorX;
		prevAnchorY = anchorY;
		prevSkewX = skewX;
		prevSkewY = skewY;
		prevScaleX = scaleX;
		prevScaleY = scaleY;
		prevStatus = status;
		prevAlpha = alpha;
		prevVisible = visible;

		time = frame->getTime();

		it++;
	}

	target->stopActionByTag(this->getTag());

	if (actions->count() > 0) {

		CCActionInterval * sequence = NULL;

		if (actions->count() == 1) {
			sequence =
					dynamic_cast<CCActionInterval*>(actions->objectAtIndex(0));
		} else {
			sequence = dynamic_cast<CCActionInterval*>(CCSequence::create(
					actions));
		}

		if (isStartWithReverse) {
			sequence = sequence->reverse();
		}

		if (this->m_loop != 0 && this->m_loopMode == LOOP_REVERSE) {
			CCActionInterval* reverseAction = sequence->reverse();

			if (reverseAction) {
				sequence = dynamic_cast<CCActionInterval*>(CCSequence::create(
						sequence, reverseAction, NULL));
			} else {
				cout << this->getTargetId() << " sequence REVERSE is null"
						<< endl;
			}

		}

		if (this->m_loop > 0) {
			sequence = dynamic_cast<CCActionInterval*>(CCSequence::create(
					CCRepeat::create(sequence, this->m_loop),
					CCCallFuncND::create(this,
					callfuncND_selector(KKAnimation::onStopAnimation), frame),
					NULL));

		} else if (this->m_loop < 0) {
			sequence = CCRepeatForever::create(sequence);
		} else {
			sequence = dynamic_cast<CCActionInterval*>(CCSequence::create(
					sequence, CCCallFuncND::create(this,
					callfuncND_selector(KKAnimation::onStopAnimation), frame),
					NULL));
		}

		sequence->setTag(this->getTag());

		target->runAction(sequence);

	} else {
		CCLog("%d has not frames \n", this->getTargetId());
	}

}

CCActionEase* KKAnimation::getActionEase(KKStoryboardFrame* frame,
		CCAction* frameAction) {

	CCActionEase* easeAction = NULL;

	CCActionInterval* intervalsAction =
			dynamic_cast<CCActionInterval*>(frameAction);

	if (intervalsAction) {

		switch (frame->getEase()) {
		case KK_EASE_IN:
			easeAction = CCEaseIn::create(intervalsAction,
					frame->getEaseRate());
			break;
		case KK_EASE_OUT:
			easeAction = CCEaseOut::create(intervalsAction,
					frame->getEaseRate());
			break;
		case KK_EASE_INOUT:
			easeAction = CCEaseInOut::create(intervalsAction,
					frame->getEaseRate());
			break;
		case KK_EASE_SIN_IN:
			easeAction = CCEaseSineIn::create(intervalsAction);
			break;

		case KK_EASE_SIN_OUT:
			easeAction = CCEaseSineOut::create(intervalsAction);
			break;

		case KK_EASE_SIN_INOUT:
			easeAction = CCEaseSineInOut::create(intervalsAction);
			break;

		case KK_EASE_BOUNCE_IN:
			easeAction = CCEaseBounceIn::create(intervalsAction);
			break;

		case KK_EASE_BOUNCE_OUT:
			easeAction = CCEaseBounceOut::create(intervalsAction);
			break;

		case KK_EASE_BOUNCE_INOUT:
			easeAction = CCEaseBounceInOut::create(intervalsAction);
			break;

		case KK_EASE_ELASTIC_IN:
			easeAction = CCEaseElasticIn::create(intervalsAction,
					frame->getEaseRate());
			break;

		case KK_EASE_ELASTIC_OUT:
			easeAction = CCEaseElasticOut::create(intervalsAction,
					frame->getEaseRate());
			break;

		case KK_EASE_ELASTIC_INOUT:
			easeAction = CCEaseElasticInOut::create(intervalsAction,
					frame->getEaseRate());
			break;
		default:
			break;
		}
	}

	return easeAction;
}

void KKAnimation::onStartAnimation(CCNode* target, void* firstFrame) {

	if (m_startEvent) {

		KKEventActionArgs args;

		args.setData(target);

		args.setSource(dynamic_cast<KKNode*>(target));

		m_startEvent->fire(&args);
	}

	KKStoryboardFrame* frame = (KKStoryboardFrame*) firstFrame;

	target->setAnchorPoint(ccp(frame->getAnchorX(), frame->getAnchorY()));

	if (!frame->getIsPositionRelative()) {
		KKNode* kknode = dynamic_cast<KKNode*>(target);
		target->setPosition(
		ccp(frame->getPositionX(kknode, kknode->getX()),
				frame->getPositionY(kknode, kknode->getY())));
	}

	if (!frame->getIsSizeRelative()) {
		target->setScaleX(frame->getWidth() / target->getContentSize().width);
		target->setScaleY(frame->getHeight() / target->getContentSize().height);
	}

	target->setRotation(frame->getAngle());

	target->setSkewX(frame->getSkewX());
	target->setSkewY(frame->getSkewY());

	if (frame->getIsEnabled()) {

		//CCLog("onstart  %s \n",KKTime::getCurrentTime().format());

		target->setVisible(frame->getIsVisible());
	}

	string status = frame->getStatus();

	CCRGBAProtocol* rgbaProtocol = dynamic_cast<CCRGBAProtocol*>(target);

	if (rgbaProtocol) {
		rgbaProtocol->setOpacity(frame->getAlpha());
	}

	KKSprite* sprite = dynamic_cast<KKSprite*>(target);
	if (sprite) {

		if (!status.empty()) {
			sprite->animate(status.c_str(), frame->getStatusRepeat(), true);
		}
	}

}

void KKAnimation::onUpdateSpriteAnimation(CCNode* target, void* updateFrame) {

	if (target && updateFrame) {

		KKStoryboardFrame* frame = (KKStoryboardFrame*) updateFrame;

		KKSprite* sprite = dynamic_cast<KKSprite*>(target);

		KKNode* kknode = dynamic_cast<KKNode*>(target);

		if (kknode) {
			kknode->setAnchorPointNotChangePosition(
			ccp(frame->getAnchorX(), frame->getAnchorY()));
		}

		if (frame->getIsEnabled()) {

			//	CCLog("onupdate %s \n",KKTime::getCurrentTime().format());

			target->setVisible(frame->getIsVisible());
		}

		if (sprite) {

			if (frame->getStatus().empty()) {
				sprite->stopActionByTag(KKSprite::ANIMATION_TAG);
				sprite->resetTexture();
			} else {
				sprite->animate(frame->getStatus().c_str(),
						frame->getStatusRepeat(), true);
			}
		}

		if (frame->getEvent()) {

			KKEventActionArgs args;

			args.setData(frame);

			args.setSource(dynamic_cast<KKNode*>(target));

			frame->getEvent()->fire(&args);
		}
	}
}

void KKAnimation::onStopAnimation(CCNode* target, void* lastFrame) {

	if (m_endEvent) {

		KKEventActionArgs args;

		args.setData(target);

		args.setSource(dynamic_cast<KKNode*>(target));

		m_endEvent->fire(&args);
	}
}

KKAnimation* KKAnimation::animationWithXml(TiXmlElement* element) {

	KKAnimation* animation = new KKAnimation();

	if (animation->initWithXml(element)) {
		animation->retain();
		animation->autorelease();
		return animation;
	}

	CC_SAFE_DELETE(animation);

	return NULL;
}

bool KKAnimation::initWithXml(TiXmlElement* element) {
	this->m_targetId = atoi(element->Attribute("id"));
	this->m_targetName = element->Attribute("target");
	this->m_loop = atoi(element->Attribute("loop"));
	this->m_loopMode = LOOP_RESTART;
	string loopMode = element->Attribute("loopmode");

	if (loopMode == "reverse") {
		this->m_loopMode = LOOP_REVERSE;
	}

	TiXmlElement* frameElment = element->FirstChildElement("frame");

	KKStoryboardFrame* frame = NULL;

	while (frameElment) {
		frame = KKStoryboardFrame::frameWithXml(frameElment);
		if (frame) {
			this->m_frames->addObject(frame);
		}
		frameElment = frameElment->NextSiblingElement("frame");
	}

	TiXmlElement* endEventElment = element->FirstChildElement("end-actions");

	if (endEventElment) {

		endEventElment->SetAttribute("name", "end-actions");
		this->m_endEvent = KKEvent::eventWithXml(endEventElment);
	}

	TiXmlElement* startEventElment = element->FirstChildElement(
			"start-actions");

	if (startEventElment) {

		startEventElment->SetAttribute("name", "start-actions");
		this->m_startEvent = KKEvent::eventWithXml(startEventElment);
	}

	/*CCLog("load animation %d ,framecount=%d \n", this->m_targetId,
	 this->m_frames->count());*/

	return this->m_frames->count() > 1;
}

KKStoryboardFrame* KKStoryboardFrame::frameWithXml(TiXmlElement* element) {
	KKStoryboardFrame* frame = new KKStoryboardFrame();

	if (frame->initWithXml(element)) {
		frame->retain();
		frame->autorelease();
		return frame;
	}

	CC_SAFE_DELETE(frame);

	return NULL;
}

bool KKStoryboardFrame::initWithXml(TiXmlElement* element) {

	this->m_time = atof(element->Attribute("time")) / 1000;
	this->m_x = atoi(element->Attribute("x")) / CC_CONTENT_SCALE_FACTOR();
	this->m_y = atoi(element->Attribute("y")) / CC_CONTENT_SCALE_FACTOR();
	this->m_width = atoi(
			element->Attribute("width")) / CC_CONTENT_SCALE_FACTOR();
	this->m_height = atoi(
			element->Attribute("height")) / CC_CONTENT_SCALE_FACTOR();
	this->m_angle = atoi(element->Attribute("angle"));
	this->m_alpha = atof(element->Attribute("alpha"));
	this->m_anchorX = atof(element->Attribute("anchorX"));
	this->m_anchorY = atof(element->Attribute("anchorY"));
	this->m_skewX = atof(element->Attribute("skewX")) * this->m_width
			/ CC_CONTENT_SCALE_FACTOR()/ 4;
	this->m_skewY = atof(element->Attribute("skewY")) * this->m_height
			/ CC_CONTENT_SCALE_FACTOR()/ 4;
	const char* status = element->Attribute("status");

	if (status) {
		this->m_status = status;
		this->m_statusRepeat = atoi(element->Attribute("statusLoop"));
	}

	const char* xtype = element->Attribute("x-type");

	if (xtype) {
		this->m_xType = atoi(xtype);
		this->m_xValue = XmlUtils::getFloatAttr(element, "x-value");
	} else {
		this->m_xType = POSITION_TYPE_LEFT;
	}

	const char* ytype = element->Attribute("y-type");

	if (ytype) {
		this->m_yType = atoi(ytype);
		this->m_yValue = XmlUtils::getFloatAttr(element, "y-value");
	} else {
		this->m_yType = POSITION_TYPE_BOTTOM;
	}

	this->m_visible = XmlUtils::getBoolAttr(element, "visible", false);
	this->m_enabled = XmlUtils::getBoolAttr(element, "enable", false);
	this->m_positionRelative = XmlUtils::getBoolAttr(element, "relPos", false);
	this->m_sizeRelative = XmlUtils::getBoolAttr(element, "relSize", false);

	this->m_ease = KK_EASE_LINE;

	const char* easeType = element->Attribute("ease");

	if (easeType) {
		if (strcmp("IN", easeType) == 0) {
			this->m_ease = KK_EASE_IN;
		} else if (strcmp("OUT", easeType) == 0) {
			this->m_ease = KK_EASE_OUT;
		} else if (strcmp("INOUT", easeType) == 0) {
			this->m_ease = KK_EASE_INOUT;
		} else if (strcmp("SIN_IN", easeType) == 0) {
			this->m_ease = KK_EASE_SIN_IN;
		} else if (strcmp("SIN_OUT", easeType) == 0) {
			this->m_ease = KK_EASE_SIN_OUT;
		} else if (strcmp("SIN_INOUT", easeType) == 0) {
			this->m_ease = KK_EASE_SIN_INOUT;
		} else if (strcmp("BOUNCE_IN", easeType) == 0) {
			this->m_ease = KK_EASE_BOUNCE_IN;
		} else if (strcmp("BOUNCE_OUT", easeType) == 0) {
			this->m_ease = KK_EASE_BOUNCE_OUT;
		} else if (strcmp("BOUNCE_INOUT", easeType) == 0) {
			this->m_ease = KK_EASE_BOUNCE_INOUT;
		} else if (strcmp("ELASTIC_IN", easeType) == 0) {
			this->m_ease = KK_EASE_ELASTIC_IN;
		} else if (strcmp("ELASTIC_OUT", easeType) == 0) {
			this->m_ease = KK_EASE_ELASTIC_OUT;
		} else if (strcmp("ELASTIC_INOUT", easeType) == 0) {
			this->m_ease = KK_EASE_ELASTIC_INOUT;
		}

		this->m_easeRate = atof(element->Attribute("easeRate")) / 10;
	}

	TiXmlElement* eventElment = element->FirstChildElement("actions");

	if (eventElment) {

		eventElment->SetAttribute("name", "actions");
		this->m_event = KKEvent::eventWithXml(eventElment);
	}

	return true;
}

float KKStoryboardFrame::getPositionX(KKNode* kknode, float prevX) {
	KKStoryboardFrame* frame = this;
	float x = frame->getX();
	if (frame->getXType() == POSITION_TYPE_RIGHT) {
		x = kknode->getKKParent()->getWidth() - frame->getXValue();
	} else if (frame->getXType() == POSITION_TYPE_CENTER) {
		x = kknode->getKKParent()->getWidth() / 2 - frame->getXValue();
	} else if (frame->getXType() == POSITION_TYPE_REL) {
		x = prevX + frame->getXValue();
	}

	return x;
}

float KKStoryboardFrame::getPositionY(KKNode* kknode, float prevY) {
	KKStoryboardFrame* frame = this;
	float y = frame->getY();

	if (frame->getYType() == POSITION_TYPE_TOP) {
		y = kknode->getKKParent()->getHeight() - frame->getYValue();
	} else if (frame->getYType() == POSITION_TYPE_CENTER) {
		y = kknode->getKKParent()->getHeight() / 2 - frame->getYValue();
	} else if (frame->getYType() == POSITION_TYPE_REL) {
		y = prevY + frame->getYValue();
	}
	return y;
}

float KKStoryboardFrame::getSizeWidth(KKNode* kknode, float prevWidth) {
	KKStoryboardFrame* frame = this;
	float width = frame->getWidth();
	if (frame->getIsSizeRelative()) {

	}

	return width;
}

float KKStoryboardFrame::getSizeHeight(KKNode* kknode, float prevHeight) {
	KKStoryboardFrame* frame = this;
	float height = frame->getHeight();

	if (frame->getIsSizeRelative()) {

	}

	return height;
}
